package com.elric.mall.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.elric.mall.common.consts.SymbolConstant;
import com.elric.mall.common.enums.FileInfoExceptionEnum;
import com.elric.mall.common.exception.ApiException;
import com.elric.mall.common.exception.LibreOfficeException;
import com.elric.mall.common.service.FileOperator;
import com.elric.mall.common.utils.DownloadUtil;
import com.elric.mall.common.utils.LibreOfficeUtil;
import com.elric.mall.dto.FileAntdDto;
import com.elric.mall.dto.FileInfoResultDto;
import com.elric.mall.mapper.UmsFileInfoMapper;
import com.elric.mall.mapper.UmsFileRelationMapper;
import com.elric.mall.model.UmsFileInfo;
import com.elric.mall.model.UmsFileInfoExample;
import com.elric.mall.model.UmsFileRelation;
import com.elric.mall.model.UmsFileRelationExample;
import com.elric.mall.service.UmsFileInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 文件信息表 服务实现类
 *
 * @author yubaoshan
 * @date 2020/6/7 22:15
 */
@Service
public class UmsFileInfoServiceImpl implements UmsFileInfoService {

    @Autowired
    private FileOperator fileOperator;

    @Autowired
    private UmsFileInfoMapper umsFileInfoMapper;

    @Autowired
    private UmsFileRelationMapper umsFileRelationMapper;

    public static final String DEFAULT_BUCKET = "defaultBucket";


    private void init(UmsFileInfo umsFileInfo) {
        umsFileInfo.setId(IdUtil.fastUUID());
        umsFileInfo.setCreateTime(new DateTime());
        umsFileInfo.setUpdateTime(new DateTime());
    }

    @Override
    public List<UmsFileInfo> list(UmsFileInfo umsFileInfo) {
        UmsFileInfoExample example = new UmsFileInfoExample();
        return umsFileInfoMapper.selectByExample(example);
    }

    @Override
    public void add(UmsFileInfo umsFileInfo) {
        init(umsFileInfo);
        umsFileInfoMapper.insert(umsFileInfo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(String fileId) {

        // 查询文件的信息
        UmsFileInfo sysFileInfo = umsFileInfoMapper.selectByPrimaryKey(fileId);

        // 删除文件记录
        umsFileInfoMapper.deleteByPrimaryKey(fileId);

        // 删除具体文件
        this.fileOperator.deleteFile(sysFileInfo.getFileBucket(), sysFileInfo.getFileObjectName());
    }

    @Override
    public void edit(UmsFileInfo umsFileInfo) {
        umsFileInfo.setUpdateTime(new DateTime());
        umsFileInfoMapper.updateByPrimaryKey(umsFileInfo);
    }

    @Override
    public UmsFileInfo detail(String id) {
        return umsFileInfoMapper.selectByPrimaryKey(id);
    }

    @Override
    public String uploadFile(MultipartFile file) {

        // 生成文件的唯一id
        String fileId = IdUtil.fastUUID();

        // 获取文件原始名称
        String originalFilename = file.getOriginalFilename();

        // 获取文件后缀
        String fileSuffix = null;

        if (ObjectUtil.isNotEmpty(originalFilename)) {
            fileSuffix = StrUtil.subAfter(originalFilename, SymbolConstant.PERIOD, true);
        }
        // 生成文件的最终名称
        String finalName = fileId + SymbolConstant.PERIOD + fileSuffix;

        // 存储文件
        byte[] bytes;
        try {
            bytes = file.getBytes();
            fileOperator.storageFile(DEFAULT_BUCKET, finalName, bytes);
        } catch (IOException e) {
            throw new ApiException(FileInfoExceptionEnum.ERROR_FILE);
        }

        // 计算文件大小kb
        long fileSizeKb = Convert.toLong(NumberUtil.div(new BigDecimal(file.getSize()), BigDecimal.valueOf(1024))
                .setScale(0, BigDecimal.ROUND_HALF_UP));

        //计算文件大小信息
        String fileSizeInfo = FileUtil.readableFileSize(file.getSize());

        // 存储文件信息
        UmsFileInfo sysFileInfo = new UmsFileInfo();
        sysFileInfo.setId(fileId);
        sysFileInfo.setFileLocation(4);
        sysFileInfo.setFileBucket(DEFAULT_BUCKET);
        sysFileInfo.setFileObjectName(finalName);
        sysFileInfo.setFileOriginName(originalFilename);
        sysFileInfo.setFileSuffix(fileSuffix);
        sysFileInfo.setFileSizeKb(fileSizeKb);
        sysFileInfo.setFileSizeInfo(fileSizeInfo);
        sysFileInfo.setCreateTime(new DateTime());
        umsFileInfoMapper.insert(sysFileInfo);

        // 返回文件id
        return fileId;
    }

    @Override
    public FileInfoResultDto getFileInfoResult(String fileId) {
        byte[] fileBytes;
        // 获取文件名
        UmsFileInfo sysFileInfo = umsFileInfoMapper.selectByPrimaryKey(fileId);
        if (sysFileInfo == null) {
            throw new ApiException(FileInfoExceptionEnum.NOT_EXISTED_FILE);
        }
        try {
            // 返回文件字节码
            fileBytes = fileOperator.getFileBytes(DEFAULT_BUCKET, sysFileInfo.getFileObjectName());
        } catch (Exception e) {
            throw new ApiException(FileInfoExceptionEnum.FILE_STREAM_ERROR);
        }

        FileInfoResultDto sysFileInfoResult = new FileInfoResultDto();
        BeanUtil.copyProperties(sysFileInfo, sysFileInfoResult);
        sysFileInfoResult.setFileBytes(fileBytes);

        return sysFileInfoResult;
    }

    @Override
    public void assertFile(String field) {
        UmsFileInfo sysFileInfo = umsFileInfoMapper.selectByPrimaryKey(field);
        if (ObjectUtil.isEmpty(sysFileInfo)) {
            throw new ApiException(FileInfoExceptionEnum.NOT_EXISTED);
        }
    }

    @Override
    public void preview(String fileId, HttpServletResponse response) {

        byte[] fileBytes;
        //根据文件id获取文件信息结果集
        FileInfoResultDto sysFileInfoResult = this.getFileInfoResult(fileId);
        //获取文件后缀
        String fileSuffix = sysFileInfoResult.getFileSuffix().toLowerCase();
        //获取文件字节码
        fileBytes = sysFileInfoResult.getFileBytes();
        //如果是图片类型，则直接输出
        if (LibreOfficeUtil.isPic(fileSuffix)) {
            try {
                //设置contentType
                response.setContentType(MediaType.IMAGE_JPEG_VALUE);
                //获取outputStream
                ServletOutputStream outputStream = response.getOutputStream();
                //输出
                IoUtil.write(outputStream, true, fileBytes);
            } catch (IOException e) {
                throw new ApiException(FileInfoExceptionEnum.PREVIEW_ERROR_NOT_SUPPORT);
            }

        } else if (LibreOfficeUtil.isDoc(fileSuffix)) {
            try {
                //如果是文档类型，则使用libreoffice转换为pdf或html
                InputStream inputStream = IoUtil.toStream(fileBytes);

                //获取目标contentType（word和ppt和text转成pdf，excel转成html)
                String targetContentType = LibreOfficeUtil.getTargetContentTypeBySuffix(fileSuffix);

                //设置contentType
                response.setContentType(targetContentType);

                //获取outputStream
                ServletOutputStream outputStream = response.getOutputStream();

                //转换
                LibreOfficeUtil.convertToPdf(inputStream, outputStream, fileSuffix);

                //输出
                IoUtil.write(outputStream, true, fileBytes);
            } catch (IOException e) {
                throw new ApiException(FileInfoExceptionEnum.PREVIEW_ERROR_NOT_SUPPORT);

            } catch (LibreOfficeException e) {
                throw new ApiException(FileInfoExceptionEnum.PREVIEW_ERROR_LIBREOFFICE);
            }

        } else {
            //否则不支持预览（暂时）
            throw new ApiException(FileInfoExceptionEnum.PREVIEW_ERROR_NOT_SUPPORT);
        }
    }

    @Override
    public void download(String id, HttpServletResponse response) {
        // 获取文件信息结果集
        FileInfoResultDto sysFileInfoResult = this.getFileInfoResult(id);
        String fileName = sysFileInfoResult.getFileOriginName();
        byte[] fileBytes = sysFileInfoResult.getFileBytes();
        DownloadUtil.download(fileName, fileBytes, response);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int addRelation(String id, String type, List<String> fileIds) {
        // 先清空已有的关联关系
        UmsFileRelationExample relationExample = new UmsFileRelationExample();
        relationExample.createCriteria().andTypeEqualTo(type).andRelIdEqualTo(id);
        umsFileRelationMapper.deleteByExample(relationExample);

        if (CollectionUtil.isNotEmpty(fileIds)) {
            // 构造关联关系清单
            List<UmsFileRelation> list = fileIds.stream().map(i -> {
                UmsFileRelation r = new UmsFileRelation();
                r.setCreateTime(new DateTime());
                r.setType(type);
                r.setId(IdUtil.fastUUID());
                r.setFileId(i);
                r.setRelId(id);
                return r;
            }).collect(Collectors.toList());
            return umsFileRelationMapper.insertBatch(list);
        }
        return 0;
    }

    @Override
    public List<FileAntdDto> findAntdFileById(String id) {
        return umsFileRelationMapper.selectByRelationKey(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delFileByEntityId(String id) {
        // 清空已有的关联关系
        UmsFileRelationExample relationExample = new UmsFileRelationExample();
        relationExample.createCriteria().andRelIdEqualTo(id);
        umsFileRelationMapper.deleteByExample(relationExample);

        // 查询文件的信息
        List<UmsFileInfo> infos = umsFileInfoMapper.selectByEntityId(id);
        if (CollectionUtil.isNotEmpty(infos)) {
            UmsFileInfoExample infoExample = new UmsFileInfoExample();
            infoExample.createCriteria().andIdIn(infos.stream().map(UmsFileInfo::getId).collect(Collectors.toList()));
            // 删除文件记录
            umsFileInfoMapper.deleteByExample(infoExample);
            // 删除具体文件
            infos.stream().forEach(i -> {
                this.fileOperator.deleteFile(i.getFileBucket(), i.getFileObjectName());
            });
        }
    }
}
